with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;
with Ada.Strings.Unbounded;
package BODY Partie is

   package SU renames Ada.Strings.Unbounded;
   use type SU.Unbounded_String;

   -----------------------------------------------------------------------------------------------------------
   ---------                                 Coup_Aleatoire                                -------------------
   -----------------------------------------------------------------------------------------------------------
   Procedure Coup_Aleatoire(Un_Plateau: in out Plateaux.plateau; X: out Plateaux.Colonne; Y: out Plateaux.Ligne)is
      Subtype Rand_Range is Integer range 1 .. 9;
      package Rand_Int is new Ada.Numerics.Discrete_Random(Rand_Range);
      seed : Rand_Int.Generator;
      Sucess:boolean:=False;
      Index_Cellule_Libre:Rand_Range:=1;
      Indice:Integer;
      Max:Integer;
   begin
      Rand_Int.Reset(seed);
      Indice:= (Integer'(Rand_Int.Random(seed)));
      Max:=9-Plateaux.Get_Nb_Tours(Un_Plateau);
      Indice:=(Indice mod Max)+1;
      for Yo in reverse Plateaux.Ligne
      loop
         if not Sucess then
            for Xo in Plateaux.Colonne
            loop
               If Plateaux.Libre(Un_Plateau,Xo,Yo)then
                  If Index_Cellule_Libre=Indice then
                     X:=Xo;
                     Y:=Yo;
                     Sucess:=true;
                     exit;
                  Else
                     Index_Cellule_Libre:=Index_Cellule_Libre+1;
                  End If;
               End if;
            End loop;
         End if;
      End loop;
   End Coup_Aleatoire;
   -----------------------------------------------------------------------------------------------------------
   ---------                  Fonction Jouer_Coup             HUMAIN                       -------------------
   -----------------------------------------------------------------------------------------------------------

   Procedure Jouer_Coup_Humain(Un_Plateau : in out Plateaux.Plateau; Un_Symbole: in out Cellules.Symbole; Fini: out Boolean) is
      X:Plateaux.Colonne;
      Y:Plateaux.Ligne;

   begin
      Saisir_Coup(Un_Plateau,X,Y);
      Plateaux.Tracer(Un_Plateau,X,Y,Un_Symbole);
      Fini:=Plateaux.Tester(Un_Plateau,X,Y,Un_Symbole); -- booleen testant la fin de partie
   end Jouer_Coup_Humain;

   -----------------------------------------------------------------------------------------------------------
   ---------                  Fonction Jouer Coup IA            DIFFICILE                  -------------------
   -----------------------------------------------------------------------------------------------------------


   Procedure Jouer_Coup_IA_Difficile(Un_Plateau : in out Plateaux.Plateau; Un_Symbole: in out Cellules.Symbole;Fini: out Boolean) is
      Next:Boolean:=False;
   BEGIN
      for Y in reverse Plateaux.Ligne
      loop
         if not Next then
            for X in Plateaux.Colonne
            loop
               --Ada.Text_IO.Put_Line("Attaque : " & boolean'Image(Plateaux.Libre(Un_Plateau,X,Y)) & " " & boolean'Image(Plateaux.Tester(Un_Plateau,X,Y,Un_Symbole)));
               --Ada.Text_IO.Put_Line("Defense : " & boolean'Image(Plateaux.Libre(Un_Plateau,X,Y)) & " " &  boolean'Image(Plateaux.Tester(Un_Plateau,X,Y,Cellules.Suivant(Un_Symbole))));
               If Plateaux.Libre(Un_Plateau,X,Y)and then  Plateaux.Tester(Un_Plateau,X,Y,Un_Symbole)then --  on regarde si on peut finir la partie
                  --Ada.Text_IO.Put_Line("Attaque : L'IA va jouer : " & X & Y); -- Permet de vérifier la position jouée par l'IA
                  Plateaux.Tracer(Un_Plateau,X,Y,Un_Symbole); -- Si oui, alors on place le pion
                  Fini:=Plateaux.Tester(Un_Plateau,X,Y,Un_Symbole); -- booleen testant la fin de partie
                  Next:=True;
                  exit;
               End if;
            End loop;
         End If;
      End loop;
      for Y in reverse Plateaux.Ligne
      loop
         if not Next then
            for X in Plateaux.Colonne
            loop
               If Plateaux.Libre(Un_Plateau,X,Y)and then  Plateaux.Tester(Un_Plateau,X,Y,Cellules.Suivant(Un_Symbole))then -- ensuite on regarde si on peut empecher l'autre joueur de finir la partie
                  --Ada.Text_IO.Put_Line("Defense : L'IA va jouer : " & X & Y); -- Permet de vérifier la position jouée par l'IA
                  Plateaux.Tracer(Un_Plateau,X,Y,Un_Symbole); -- Si oui, alors on place le pion
                  Fini:=Plateaux.Tester(Un_Plateau,X,Y,Un_Symbole); -- booleen testant la fin de partie
                  Next:=True;
                  exit;
               End if;
            End loop;
         End If;
      End loop;
      If not Next then
         Ada.Text_IO.Put_Line("Random");
         Jouer_Coup_IA_Facile(Un_Plateau,Un_Symbole,Fini);
      End if;
   END Jouer_Coup_IA_Difficile;

   -----------------------------------------------------------------------------------------------------------
   ---------                  Fonction Jouer Coup IA            MOYEN                      -------------------
   -----------------------------------------------------------------------------------------------------------

   Procedure Jouer_Coup_IA_Moyen(Un_Plateau : in out Plateaux.Plateau; Un_Symbole: in out Cellules.Symbole;Fini: out Boolean) is
      Type Rand_Range is range 1..2;
      package Rand_Int is new Ada.Numerics.Discrete_Random(Rand_Range);
      seed : Rand_Int.Generator;
      Choix : Rand_Range;
   begin
      Rand_Int.Reset(seed);
      Choix := Rand_Int.Random(seed);
      If (Choix=1) then
         Jouer_Coup_IA_Facile(Un_Plateau,Un_Symbole,Fini);
      Else
         Jouer_Coup_IA_Difficile(Un_Plateau,Un_Symbole,Fini);
      End if;
   end Jouer_Coup_IA_Moyen;

   -----------------------------------------------------------------------------------------------------------
   ---------                   Fonction Jouer Coup IA        FACILE                        -------------------
   -----------------------------------------------------------------------------------------------------------

   Procedure Jouer_Coup_IA_Facile(Un_Plateau : in out Plateaux.Plateau; Un_Symbole: in out Cellules.Symbole;Fini: out Boolean) is
      X:Plateaux.Colonne;
      Y:Plateaux.Ligne;
   Begin
      Partie.Coup_Aleatoire(Un_Plateau,X,Y);
      Plateaux.Tracer(Un_Plateau,X,Y,Un_Symbole);
      Fini:=Plateaux.Tester(Un_Plateau,X,Y,Un_Symbole); -- booleen testant la fin de partie
   End Jouer_Coup_IA_Facile;
   -----------------------------------------------------------------------------------------------------------
   ---------                  Fonction pour saisir les coups (Terminal)                    -------------------
   -----------------------------------------------------------------------------------------------------------


   Procedure Saisir_Coup(Un_Plateau: in out Plateaux.plateau; X: out Plateaux.Colonne; Y: out Plateaux.Ligne) is
      Succes : Boolean := False;
      Xo:Plateaux.Colonne;Yo:Plateaux.Ligne;
   Begin

      Ada.Text_IO.Put_Line("Fonction de saisie des coups");
      While Succes=False Loop
         Begin
            Loop -- Boucle demandant de saisir la ligne / Redemande si saisie incorrecte
               begin
                  Ada.Text_Io.Put ("Colonne desiree ? : "); Ada.Text_Io.Get (Xo); exit;
               exception
                  when others =>Ada.Text_Io.Put_Line ("Veuillez saisir un chiffre entre A et C");
               End;
            End Loop;

            Loop -- Boucle demandant de saisir la colonne / Redemande si saisie incorrecte
               begin
                  Ada.Text_Io.Put ("Ligne desiree ?  : "); Ada.Text_Io.Get (Yo);Ada.Text_IO.Put_Line(""); exit;
               exception
                  when others => Ada.Text_Io.Put_Line ("Veuillez saisir une Lettre entre 1 et 3");
               End;
            End Loop;
            --Ada.Text_Io.Put_Line ("Xo : " & Xo & "Yo : " & Yo);

            If Plateaux.Libre(Un_Plateau,Xo,Yo) Then -- Si la case est libre la saisie est correcte, on sort de la boucle
               Succes:= true;
            Else
               Ada.Text_Io.Put_Line ("Cette position a deja ete jouee.");
            End If;
         End;
      End Loop;
      X:=Xo;Y:=Yo;
   End Saisir_Coup;

   -----------------------------------------------------------------------------------------------------------
   ---------                             Fonction Jouer_Coup                               -------------------
   -----------------------------------------------------------------------------------------------------------

   Procedure Jouer_Coup(Grille : in out Plateaux.Plateau; J : Joueurs.Joueur; Fini: out Boolean) is
      Sbl:Cellules.Symbole;
      Use Joueurs;
   begin
      Sbl:=Joueurs.Get_Symbole(J);
      If Joueurs.Get_Genre(J) =Joueurs.Genre'(Humain) Then
         Partie.Jouer_Coup_Humain(Grille,Sbl,Fini);
      ElsIf Joueurs.Get_Genre(J) =Joueurs.Genre'(Ordinateur_Facile) Then
         Partie.Jouer_Coup_IA_Facile(Grille,Sbl,Fini);
      Elsif Joueurs.Get_Genre(J) =Joueurs.Genre'(Ordinateur_Moyen) Then
         Partie.Jouer_Coup_IA_Moyen(Grille,Sbl,Fini);
      ElsIf Joueurs.Get_Genre(J) =Joueurs.Genre'(Ordinateur_Difficile) Then
         Partie.Jouer_Coup_IA_Difficile(Grille,Sbl,Fini);
      End if;
   end Jouer_Coup;

   -------------------------------------------------------------------------------
   ------                        AFFICHER                                ---------
   -------------------------------------------------------------------------------
   procedure Afficher(Un_Plateau : Plateaux.Plateau) is

      function Image (Un_Symbole : Cellules.Symbole) return Character is

         S : constant String (1 .. 3) := Cellules.Symbole'Image(Un_Symbole);

      begin  return S (2); end;

   begin
      for Y in reverse Plateaux.Ligne
      loop
         Ada.Text_IO.Put (Y);
         Ada.Text_IO.Put (" |");
         for X in Plateaux.Colonne
         loop
            if Plateaux.Libre(Un_Plateau, X, Y) then
               Ada.Text_IO.Put (" |");
            else
               Ada.Text_IO.Put (Image(Plateaux.Occupant(Un_Plateau, X, Y))&"|");
            end if;
         end loop;
         ada.Text_IO.New_Line;
      end loop;
      Ada.Text_IO.New_Line;
      Ada.Text_IO.Put (' '); for X in Plateaux.Colonne
      loop Ada.Text_IO.Put (' '); Ada.Text_IO.Put (X &' '); end loop; Ada.Text_IO.New_Line;

   end Afficher;
   -----------------------------------------------------------------------------
   --------                      Jouer Partie                         ----------
   -----------------------------------------------------------------------------



   Procedure Jouer_Partie(J1: in Joueurs.Joueur; J2: in Joueurs.Joueur) is
      Grille:Plateaux.Plateau;
      Sbl:Cellules.Symbole;
      Fini:Boolean:=False;
      JoueurCourant:SU.Unbounded_String;
      Use Joueurs;
   Begin


      Ada.Text_IO.Put_Line("Lancement de Partie");
      Ada.Text_Io.Put("Choix du signe :"); Sbl:= Joueurs.Get_Symbole(J1);
      Ada.Text_IO.Put_Line(" ");
      while not Plateaux.Plein(Grille) and then not Fini loop-- On joue jusqu'a ce qu'un joueur gagne ou que la grille soit pleine.
         Begin
            Ada.Text_IO.Put_Line("Tour n" & Natural'Image(Plateaux.Get_Nb_Tours(Grille)) & " - " & Joueurs.Get_Nom(J1));
            Partie.Afficher(Grille);
            Partie.Jouer_Coup(Grille,J1,Fini);
            JoueurCourant:=SU.To_Unbounded_String(Joueurs.Get_Nom(J1));
            if not Plateaux.Plein(Grille) and then not Fini then
               Ada.Text_IO.Put_Line("Tour n" & Natural'Image(Plateaux.Get_Nb_Tours(Grille)) & " - " & Joueurs.Get_Nom(J2));
               Partie.Afficher(Grille);
               Partie.Jouer_Coup(Grille,J2,Fini);
               JoueurCourant:=SU.To_Unbounded_String(Joueurs.Get_Nom(J2));
            End if;
         End;
      End Loop;

      Partie.Afficher(Grille);

      If(Fini) Then -- Si un joueur a gagne
         Ada.Text_IO.Put_Line( Su.To_String(JoueurCourant) & " gagne la partie !");
      Else --sinon partie nulle
         Ada.Text_IO.Put_Line(" Partie Nulle !");
      End If;


   End Jouer_Partie;

end Partie;
